Skip to content

feat(tasks): split AOJ client by source and improve task import UI (#3525)#3561

Merged
KATO-Hiro merged 32 commits into
stagingfrom
#3525
May 20, 2026
Merged

feat(tasks): split AOJ client by source and improve task import UI (#3525)#3561
KATO-Hiro merged 32 commits into
stagingfrom
#3525

Conversation

@KATO-Hiro
Copy link
Copy Markdown
Collaborator

@KATO-Hiro KATO-Hiro commented May 18, 2026

close #3525

Summary by CodeRabbit

  • 新機能

    • 管理画面のタスクインポートUIを全面刷新:取得元選択、非同期取得、検索・ページング付きテーブルから個別インポート可能に。管理側でのタスク作成/成績更新アクションを追加。
  • バグ修正

    • AtCoder/AOJ由来のコンテスト名・タスク名の前後空白を自動削除。
  • テスト

    • 取得・変換挙動を検証するテストとフィクスチャを大幅強化。
  • その他

    • 取得/キャッシュ/インポート処理の構成を整理・再設計。

KATO-Hiro and others added 23 commits May 17, 2026 08:49
- Split AojApiClient into AojCoursesApiClient and AojChallengesApiClient
- Reorganize AOJ fixtures into source-specific directories (courses/challenges)
- Add ContestTaskFetcher to unify per-source fetch logic
- Replace TaskListForEdit with TaskSearchBox and TaskTableForImport components
- Update task import page to support source selection and pagination

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…n vite config

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…rder

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ementation

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… flow

- Add trim() to contest/task titles from AtCoder and AOJ API clients
- Return fail() with proper HTTP status in import and update actions
- Log caught errors instead of silently swallowing them
- Surface import failure to user via error message in TaskTableForImport

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Delete task-import-by-source review.md (findings addressed in prior commit)
- Expand inline object literals in atcoder_problems.test.ts for readability

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 18, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 8526b141-2da6-4517-8731-a132d903e5bc

📥 Commits

Reviewing files that changed from the base of the PR and between a2fbb3b and ff0b07b.

📒 Files selected for processing (1)
  • src/routes/(admin)/tasks/+page.svelte

📝 Walkthrough

Walkthrough

AOJ/AtCoder client implementations reworked to use a shared cache-fetcher; many fixtures updated. Admin import UI rewritten to on-demand fetch with new actions (fetch/create/update), filter/table components and tests added. Titles from upstream APIs are trimmed.

Changes

AOJ Client Refactor & Fetcher

Layer / File(s) Summary
共通キャッシュ fetcher
src/lib/clients/contest_task_fetcher.ts
汎用 FetchContests/FetchTasks 設定型と getCachedOrFetchContests/getCachedOrFetchTasks を追加。
clients index (source map & fetch APIs)
src/lib/clients/index.ts
ContestTaskImportSource 型導入、importSources マップ、fetchContests/fetchTasks 等を追加(API metrics ログ含む)。
AOJ クライアント再構成
src/lib/clients/aizu_online_judge/clients.ts
AojCoursesApiClient/AojChallengesApiClient を HttpRequestClient+ContestTaskCache 前提で再実装し transformer を直接定義(基底クラス削除)。
AOJ 統合テスト & fixtures
src/lib/clients/aizu_online_judge/clients.test.ts, src/lib/clients/fixtures/aizu_online_judge/courses/*, src/lib/clients/fixtures/record_requests.ts
nock を用いた /courses,/problems,/challenges の経路検証へ置換。fixtures を courses 配下へ移行、record_requests の保存フロー変更。

AtCoder client & fixtures

Layer / File(s) Summary
AtCoder API title trim
src/lib/clients/atcoder/atcoder_problems.ts, src/lib/clients/atcoder/atcoder_problems.test.ts
getContests/getTasks で各要素の title を trim() して返すよう変更し、テストを追加。
AtCoder fixtures更新
src/lib/clients/fixtures/atcoder_problems/*
contests.json / tasks.json の大規模更新(配列ブロック置換)。

Admin Import Flow & UI

Layer / File(s) Summary
Server actions & merge logic
src/routes/(admin)/tasks/+page.server.ts, src/routes/(admin)/tasks/[task_id]/+page.server.ts
load を簡素化、actions に fetch を追加(fetchContests/fetchTasks と DB タスク取得を並列実行)。create/update を入力仕様に合わせて更新。
UI components & page
src/routes/(admin)/tasks/+page.svelte, src/routes/(admin)/tasks/_components/*
?/fetch を enhance する取得 UI、TaskSearchBox/TaskTableForImport を追加。検索・ページング・インポート結果ハンドリングを導入。
filter util & tests
src/routes/(admin)/tasks/_utils/contests.ts, src/routes/(admin)/tasks/_utils/contests.test.ts, vite.config.ts
filterContests 実装とテストを追加。Vitest 設定に src/routes/**/*.test.ts を含める。
小さな変更
.claude/rules/coding-style.md, src/lib/components/TaskForm.svelte
コーディング規則追記、TaskForm の action を相対化。

Sequence Diagram(s)

sequenceDiagram
  participant Admin as 管理者
  participant Page as +page.svelte
  participant Server as +page.server.ts
  participant Fetcher as contest_task_fetcher.getCachedOrFetch*
  participant Cache as ContestTaskCache
  participant HttpClient as HttpRequestClient
  participant DB as taskService

  Admin->>Page: ソース選択・?/fetch
  Page->>Server: POST ?/fetch (source)
  Server->>Fetcher: fetchContests(source)
  Fetcher->>Cache: cache.getCachedOrFetch*(fetchFn)
  Cache->>HttpClient: fetchApiWithConfig(endpoint)
  HttpClient->>Cache: API response
  Server->>DB: taskService.getTasks()
  DB->>Server: existing tasks
  Server->>Page: importContests (unregistered tasks included)
  Page->>Server: POST ?/create (tasks JSON)
  Server->>DB: taskService.createTask(...)
  DB->>Server: create results
  Server->>Page: success/failure
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Poem

AOJ は fetch を抱き、fixtures は並び替わる。
空白は消え title は澄み、テストは静かに頷く。
管理画面は選び、ページをめくる。
キャッシュは待ち、HTTP は応える。
デプロイの朝を待とう。

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning AtCoder(atcoder_problems.ts/test)への trim() 追加は #3525 の AOJ 関連要件外。UI コンポーネント新規追加や fixture 更新も関連が薄い。 AtCoder クライアント変更と UI 刷新を別 PR に分離するか、#3525 の要件範囲拡大を明示化する。
Docstring Coverage ⚠️ Warning Docstring coverage is 18.18% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed タイトルはAOJクライアント分割とタスク import UI改善という主要な変更を的確に要約している。
Linked Issues check ✅ Passed PR は #3525 の全要件を満たす。AOJ クライアント連続呼び出し設計改善・基底クラス設計見直し・enum 化による型安全性向上が実装されている。

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch #3525

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 8

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/lib/clients/fixtures/record_requests.ts`:
- Around line 53-60: The fixtures are sampled non-deterministically and can
produce tasks that reference contests not present in the sampled contests;
change the sampling to deterministic (e.g., use a seeded RNG or sort+slice) and
ensure referential integrity by filtering the sampled tasks to only include
tasks whose contest_id exists in the sampled contests before writing; update the
calls around getRandomElementsFromArray(contests, 100) and
getRandomElementsFromArray(tasks, 100) (and the other similar occurrences noted)
so you first deterministically select contests into a variable (e.g.,
sampledContests), then deterministically select or sort tasks and filter them
with sampledContests' IDs (e.g., sampledTasks = tasks.filter(t =>
sampledContestsIds.has(t.contest_id)) then slice to desired count) and pass
those filtered arrays to toJson so the output is reproducible and consistent.

In `@src/routes/`(admin)/tasks/_utils/contests.ts:
- Around line 4-6: Treat strings that are only whitespace as empty: change the
check and normalization to use trim(). Replace the current hasNoQuery and
lowerQuery logic so hasNoQuery becomes "!query || query.trim() === ''" and
lowerQuery is computed from "query.trim().toLowerCase()" (ensuring you guard for
undefined/null query before calling trim()). Update the assignments where
"hasNoQuery" and "lowerQuery" are defined (the current lines with hasNoQuery and
lowerQuery) accordingly.

In `@src/routes/`(admin)/tasks/[task_id]/+page.server.ts:
- Around line 54-80: Replace the manual formData parsing in the actions.update
handler with Superforms + Zod: create a Zod schema (e.g., TaskUpdateSchema) that
validates task_id as a nonempty string and task_grade as an optional
enum/nullable matching your TaskGrade values (or a string refined to TaskGrade),
then call superValidate(request, TaskUpdateSchema) inside update and branch on
validation result instead of ad-hoc checks; extract validated values from
result.data, map/convert task_grade to your TaskGrade type (or let Zod produce
it) and call taskService.updateTask(task_id, task_grade), and on validation
failure return the Superforms-consistent error response so errors from
actions.update follow the same Superforms + Zod pattern used elsewhere (leave
validateAdminAccess and taskService.updateTask calls intact).
- Around line 55-73: The update handler currently trusts the form field task_id
(taskId) allowing tampering; change it to use the route parameter instead: read
the task id from the handler's params (use params.task_id) and remove/ignore the
form-derived task_id, then pass that params-derived id into
taskService.updateTask; keep existing validation for task_grade (getTaskGrade)
and the bad-request/fail branches but ensure any checks reference params.task_id
(and return fail if it's missing) rather than the form value.

In `@src/routes/`(admin)/tasks/+page.server.ts:
- Around line 70-96: The handler currently trusts the client-provided tasks JSON
(tasksJson → tasks) and uses client-supplied fields (task.title,
task.contest_id, task.source) when calling taskService.createTask; instead,
validate/trust only an identifier from the client (e.g., task.id or task.source)
and re-fetch the canonical task data on the server (via your service/DB) before
creating records. Change the import loop that maps over TaskForImport so it
discards client title/contest_id and instead: lookup the authoritative task
payload using the submitted task.id/source, derive the server-side values
(server_contest_id, server_title, server_problem_index), compute the id with
sha256(contest_id + server_title) and call taskService.createTask with those
server-validated fields; ensure any missing or invalid lookups return
BAD_REQUEST or a clear failure path.
- Around line 21-104: The handlers fetch and create currently parse and validate
request.formData() manually; replace them with Superforms + Zod: define Zod
schemas (e.g. ContestImportQuerySchema with source for fetch, and
ContestImportCreateSchema with source:string, contest_id:string, tasks:string
(or structured TasksForImport) for create), use superValidate(request, schema)
in both handlers and return the validated form (or validation errors) so
Superforms handles client errors; keep the admin check
(validateAdminAccess(locals)), then in fetch use data.source from the validated
form to call fetchContests/fetchTasks and the existing
prepareTaskMap/filterUnregisteredTasks/mergeContestsAndUnregisteredTasks flow,
and in create use data.contest_id and the parsed tasks (parse JSON after
validation or use a Zod preprocessor to parse tasks into TasksForImport) and
then run the existing sha256 + taskService.createTask loop; ensure all
validation failures return the superform validation result instead of manual
fail responses and preserve try/catch for server errors with the same
INTERNAL_SERVER_ERROR handling.
- Around line 85-94: The current ID generation uses sha256(contest_id +
task.title) which risks collisions when multiple tasks share the same title;
change the seed to use the original external identifier by hashing contest_id +
task.id instead. Locate the tasks.map block where TaskForImport is processed,
replace usage of task.title in the sha256 call with task.id so the generated id
passed into taskService.createTask is derived from contest_id + task.id.

In `@src/routes/`(admin)/tasks/+page.svelte:
- Around line 94-97: 選択している取得元を切り替えたときに古い結果が残って誤インポートを誘発しているので、onchange ハンドラ内で
currentPage = 1 に加えて importContests を空配列にリセットし、インポート失敗の表示を消すためのエラー状態(例:
importError または importErrors を使っている箇所)を null/空にクリアしてください。該当箇所は +page.svelte の
onchange コールバックなので、そのブロックに importContests = [] と importError = null
を追加して一覧とエラー状態を同時に初期化してください。
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: e1ec287c-2d74-4277-a94d-231211adee9d

📥 Commits

Reviewing files that changed from the base of the PR and between 848183c and 06b7f09.

📒 Files selected for processing (30)
  • .claude/rules/coding-style.md
  • src/lib/clients/aizu_online_judge/clients.test.ts
  • src/lib/clients/aizu_online_judge/clients.ts
  • src/lib/clients/aizu_online_judge/utils.test.ts
  • src/lib/clients/aizu_online_judge/utils.ts
  • src/lib/clients/atcoder/atcoder_problems.test.ts
  • src/lib/clients/atcoder/atcoder_problems.ts
  • src/lib/clients/contest_task_fetcher.ts
  • src/lib/clients/fixtures/aizu_online_judge/challenges/jag_prelim/contests.json
  • src/lib/clients/fixtures/aizu_online_judge/challenges/jag_regional/contests.json
  • src/lib/clients/fixtures/aizu_online_judge/challenges/pck_final/contests.json
  • src/lib/clients/fixtures/aizu_online_judge/challenges/pck_prelim/contests.json
  • src/lib/clients/fixtures/aizu_online_judge/contests.json
  • src/lib/clients/fixtures/aizu_online_judge/courses/contests.json
  • src/lib/clients/fixtures/aizu_online_judge/courses/tasks.json
  • src/lib/clients/fixtures/aizu_online_judge/tasks.json
  • src/lib/clients/fixtures/atcoder_problems/contests.json
  • src/lib/clients/fixtures/atcoder_problems/tasks.json
  • src/lib/clients/fixtures/record_requests.ts
  • src/lib/clients/index.ts
  • src/lib/components/TaskForm.svelte
  • src/lib/components/TaskListForEdit.svelte
  • src/routes/(admin)/tasks/+page.server.ts
  • src/routes/(admin)/tasks/+page.svelte
  • src/routes/(admin)/tasks/[task_id]/+page.server.ts
  • src/routes/(admin)/tasks/_components/TaskSearchBox.svelte
  • src/routes/(admin)/tasks/_components/TaskTableForImport.svelte
  • src/routes/(admin)/tasks/_utils/contests.test.ts
  • src/routes/(admin)/tasks/_utils/contests.ts
  • vite.config.ts
💤 Files with no reviewable changes (3)
  • src/lib/clients/fixtures/aizu_online_judge/tasks.json
  • src/lib/components/TaskListForEdit.svelte
  • src/lib/clients/fixtures/aizu_online_judge/contests.json

Comment thread src/lib/clients/fixtures/record_requests.ts Outdated
Comment thread src/routes/(admin)/tasks/_utils/contests.ts Outdated
Comment thread src/routes/(admin)/tasks/[task_id]/+page.server.ts
Comment thread src/routes/(admin)/tasks/[task_id]/+page.server.ts Outdated
Comment thread src/routes/(admin)/tasks/+page.server.ts
Comment thread src/routes/(admin)/tasks/+page.server.ts
Comment thread src/routes/(admin)/tasks/+page.server.ts
Comment thread src/routes/(admin)/tasks/+page.svelte
KATO-Hiro and others added 2 commits May 19, 2026 13:45
- Use params.task_id instead of form data to prevent client-supplied ID injection
- Use sha256(contest_id + task.id) instead of task.title to avoid hash collisions (AOJ same-title tasks confirmed)
- Deduplicate contests in mergeContestsAndUnregisteredTasks via Set
- Trim whitespace before empty-query check in contest filter
- Replace non-deterministic random sampling with deterministic sort+slice in fixtures
- Remove getRandomElementsFromArray helper (no longer needed)
- Reset importContests and importError state on successful import

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (4)
src/routes/(admin)/tasks/+page.svelte (1)

94-98: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

取得元切替時に fetchError もクリアしてください。

  • 旧ソースの取得エラー表示が残り、切替後の状態を誤認しやすいです。
  • onchangefetchError = null を追加してください。
差分案
              onchange={() => {
                currentPage = 1;
                importContests = [];
+               fetchError = null;
                importError = null;
              }}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/routes/`(admin)/tasks/+page.svelte around lines 94 - 98, The onchange
handler for the source selector clears currentPage, importContests, and
importError but not fetchError, leaving old fetch errors visible; update the
onchange callback (the anonymous handler bound with onchange in the
+page.svelte) to also set fetchError = null so that previous fetch errors are
cleared when switching sources (i.e., alongside currentPage = 1, importContests
= [], importError = null add fetchError = null).
src/routes/(admin)/tasks/+page.server.ts (2)

21-29: 🛠️ Refactor suggestion | 🟠 Major | 🏗️ Heavy lift

フォーム検証を Superforms + Zod に統一してください。

fetch/create が手動 formData 解析のままで、バリデーションと失敗レスポンスの一貫性が崩れています。superValidate + Zod schema に寄せるべきです。
As per coding guidelines, "src/routes/**/*.{server.ts,svelte}: Use Superforms + Zod validation for forms."

Also applies to: 57-83

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/routes/`(admin)/tasks/+page.server.ts around lines 21 - 29, The fetch and
create handlers currently parse request.formData() and use
isContestTaskImportSource manually; replace these with Superforms + a Zod
schema: define a Zod schema (e.g., ContestTaskImportSchema) that validates the
source field (and any other fields used between lines 21–29 and 57–83), call
superValidate(request, ContestTaskImportSchema) in both fetch and create after
validateAdminAccess(locals), and return the superValidate result or use
fail(superForm.errors / BAD_REQUEST) on validation failure; ensure handlers use
the resulting validated.data.source instead of formData.get('source') and keep
existing guards such as validateAdminAccess(locals).

70-95: ⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

クライアント送信の tasks を信頼しないでください。

Line 79-95 で tasksJson 由来の task.contest_id / task.problem_index / task.title をそのまま永続化しており、hidden input 改変で不正登録できます。source + contest_id + task.id を基準にサーバー側で正データを再取得して登録値を確定してください(最低でも task.contest_id === contest_id 検証と createTask には contest_id を使う必要があります)。

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/routes/`(admin)/tasks/+page.server.ts around lines 70 - 95, Do not trust
client-sent tasksJson: validate and replace client fields with
server-authoritative values before calling taskService.createTask. Specifically,
when iterating tasks (TasksForImport / TaskForImport) ensure task.contest_id ===
contest_id (reject if not), compute id with sha256(contest_id + task.id) as now,
then fetch the canonical task metadata from your trusted source using contest_id
and task.id (or source+contest_id+task.id) and use those fetched values for
contest_id, problem_index, title when calling taskService.createTask (do not
pass through task.contest_id/problem_index/title from the parsed JSON).
src/routes/(admin)/tasks/[task_id]/+page.server.ts (1)

58-69: 🛠️ Refactor suggestion | 🟠 Major | 🏗️ Heavy lift

update アクションも Superforms + Zod に統一してください。

task_grade の手動パース/検証実装になっており、同ルートのフォーム検証方式が揃っていません。schema + superValidate に寄せるべきです。
As per coding guidelines, "src/routes/**/*.{server.ts,svelte}: Use Superforms + Zod validation for forms."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/routes/`(admin)/tasks/[task_id]/+page.server.ts around lines 58 - 69, The
update action currently manually reads task_grade and uses getTaskGrade; replace
this with a Zod schema for the form (e.g. const schema = z.object({ task_grade:
z.enum([...]) .optional() }) or z.union with '' mapped to undefined) and call
superValidate(request, schema) to validate; use the returned
form.data/task_grade value instead of taskGradeStr/getTaskGrade, and on
validation failure return fail(BAD_REQUEST, form) (or return success when
task_grade is empty) — update references in the action to use superValidate and
the schema, and ensure the validated task_grade is typed as TaskGrade where
needed.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/lib/clients/fixtures/record_requests.ts`:
- Line 46: This file currently exports TEST_DATA_BASE_DIR but also calls main()
at the top-level, which can cause side-effects when the module is imported;
change the module so main() is only invoked when the file is run directly by
guarding the invocation (keep the export of TEST_DATA_BASE_DIR unchanged) —
locate the top-level main() call and replace it with a guarded invocation using
the Node runtime check (e.g., require.main === module for CommonJS or the
appropriate import.meta.url check for ESM) so importing the module does not
execute main().

---

Duplicate comments:
In `@src/routes/`(admin)/tasks/[task_id]/+page.server.ts:
- Around line 58-69: The update action currently manually reads task_grade and
uses getTaskGrade; replace this with a Zod schema for the form (e.g. const
schema = z.object({ task_grade: z.enum([...]) .optional() }) or z.union with ''
mapped to undefined) and call superValidate(request, schema) to validate; use
the returned form.data/task_grade value instead of taskGradeStr/getTaskGrade,
and on validation failure return fail(BAD_REQUEST, form) (or return success when
task_grade is empty) — update references in the action to use superValidate and
the schema, and ensure the validated task_grade is typed as TaskGrade where
needed.

In `@src/routes/`(admin)/tasks/+page.server.ts:
- Around line 21-29: The fetch and create handlers currently parse
request.formData() and use isContestTaskImportSource manually; replace these
with Superforms + a Zod schema: define a Zod schema (e.g.,
ContestTaskImportSchema) that validates the source field (and any other fields
used between lines 21–29 and 57–83), call superValidate(request,
ContestTaskImportSchema) in both fetch and create after
validateAdminAccess(locals), and return the superValidate result or use
fail(superForm.errors / BAD_REQUEST) on validation failure; ensure handlers use
the resulting validated.data.source instead of formData.get('source') and keep
existing guards such as validateAdminAccess(locals).
- Around line 70-95: Do not trust client-sent tasksJson: validate and replace
client fields with server-authoritative values before calling
taskService.createTask. Specifically, when iterating tasks (TasksForImport /
TaskForImport) ensure task.contest_id === contest_id (reject if not), compute id
with sha256(contest_id + task.id) as now, then fetch the canonical task metadata
from your trusted source using contest_id and task.id (or
source+contest_id+task.id) and use those fetched values for contest_id,
problem_index, title when calling taskService.createTask (do not pass through
task.contest_id/problem_index/title from the parsed JSON).

In `@src/routes/`(admin)/tasks/+page.svelte:
- Around line 94-98: The onchange handler for the source selector clears
currentPage, importContests, and importError but not fetchError, leaving old
fetch errors visible; update the onchange callback (the anonymous handler bound
with onchange in the +page.svelte) to also set fetchError = null so that
previous fetch errors are cleared when switching sources (i.e., alongside
currentPage = 1, importContests = [], importError = null add fetchError = null).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: f17b4d0b-d0fe-4af1-8388-44e50b8e1526

📥 Commits

Reviewing files that changed from the base of the PR and between 06b7f09 and 7fc8c32.

📒 Files selected for processing (5)
  • src/lib/clients/fixtures/record_requests.ts
  • src/routes/(admin)/tasks/+page.server.ts
  • src/routes/(admin)/tasks/+page.svelte
  • src/routes/(admin)/tasks/[task_id]/+page.server.ts
  • src/routes/(admin)/tasks/_utils/contests.ts

Comment thread src/lib/clients/fixtures/record_requests.ts Outdated
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@KATO-Hiro
Copy link
Copy Markdown
Collaborator Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 19, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/routes/`(admin)/tasks/+page.svelte:
- Around line 84-105: The new form uses enhance and manual state (handleFetch,
selectedSource, isFetching, importContests, fetchError, importError) instead of
Superforms + Zod; replace it by creating a Zod schema for the fetch payload
(e.g., source) and a Superforms form on the page (useForm/useAction or loadForm
pattern), bind the Select to the Superforms form field (instead of
selectedSource) and use the form object for submission state (isSubmitting) and
server-side validation; remove the enhance hook and manual reset of
importContests/fetchError/importError, and handle success/failure through the
Superforms response and server action so validation and error handling are
unified with the rest of the route.
- Around line 109-137: The fetch error message is being hidden by the
successful-data branch because the condition "importContests.length >= 1" is
evaluated before checking "fetchError !== null"; update the rendering logic in
+page.svelte so fetchError is checked and rendered (using the existing red error
<p>) before or in priority over the importContests branch (or add a top-level
conditional that displays fetchError when set), while keeping existing
isFetching, TaskTableForImport, paginationNav, and importError behavior intact
(refer to symbols importContests, fetchError, isFetching, importError,
TaskTableForImport, paginationNav).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 8738a5f6-c010-4cc6-8f8d-b1075cac07e1

📥 Commits

Reviewing files that changed from the base of the PR and between 7fc8c32 and 38537b0.

📒 Files selected for processing (2)
  • src/lib/clients/fixtures/record_requests.ts
  • src/routes/(admin)/tasks/+page.svelte

Comment thread src/routes/(admin)/tasks/+page.svelte
Comment thread src/routes/(admin)/tasks/+page.svelte
KATO-Hiro and others added 2 commits May 19, 2026 22:03
…ing errors

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/routes/`(admin)/tasks/+page.svelte:
- Around line 30-45: The current handleFetch handler applies any arriving
response regardless of whether the user changed the selected source during the
request, causing importContests to reflect a stale source; modify handleFetch so
that when starting a fetch you capture the current selectedSource (or increment
a local requestId) and then when the async response arrives only set
importContests/searchQuery/fetchError if the captured selectedSource/requestId
still matches the current one (alternatively disable source switching while
isFetching is true); update references to importContests, isFetching, fetchError
and the logic that feeds TaskTableForImport/selectedSource to enforce this check
so stale A responses cannot overwrite a newer B selection (apply same pattern
for the other fetch block at lines 89-103).
- Around line 28-45: The custom enhance callback in handleFetch currently only
handles 'success' and 'failure', which overrides SvelteKit's default
post-processing and prevents 'redirect' and 'error' results from being handled;
update the returned async ({ result }) => { ... } handler in handleFetch to
explicitly handle result.type === 'redirect' by calling goto(result.location)
and returning, and handle result.type === 'error' by delegating to the default
action handling (e.g., call applyAction(result) or rethrow so the nearest +error
boundary renders); leave the existing branches for 'success' (set importContests
and clear searchQuery) and 'failure' (set fetchError) intact.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 7dd1ebdb-ce0a-4ade-85aa-d277eaeb898f

📥 Commits

Reviewing files that changed from the base of the PR and between 38537b0 and a2fbb3b.

📒 Files selected for processing (1)
  • src/routes/(admin)/tasks/+page.svelte

Comment thread src/routes/(admin)/tasks/+page.svelte
Comment thread src/routes/(admin)/tasks/+page.svelte
KATO-Hiro and others added 3 commits May 19, 2026 22:33
…lect while fetching

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ation

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…erated redirect

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/routes/`(admin)/tasks/+page.svelte:
- Around line 32-40: The previous import failure message persists because
importError is not cleared when a successful fetch updates importContests;
update the fetch flow (either at the start of handleFetch or inside the success
branch) to set importError = null so stale errors are removed; specifically
update the async result handler that updates importContests (the block
referencing importContests and searchQuery) and also mirror the change in the
analogous block around lines 135-137 to ensure importError is cleared on success
or when starting a new fetch.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 59d5436a-4408-4b4b-9d70-f09ed3f78d18

📥 Commits

Reviewing files that changed from the base of the PR and between a2fbb3b and a3bd243.

📒 Files selected for processing (1)
  • src/routes/(admin)/tasks/+page.svelte

Comment thread src/routes/(admin)/tasks/+page.svelte
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@KATO-Hiro
Copy link
Copy Markdown
Collaborator Author

@coderabbitai resume

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 19, 2026

✅ Actions performed

Reviews resumed.

Copy link
Copy Markdown
Collaborator Author

@KATO-Hiro KATO-Hiro left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@KATO-Hiro KATO-Hiro merged commit 40e2926 into staging May 20, 2026
3 checks passed
@KATO-Hiro KATO-Hiro deleted the #3525 branch May 20, 2026 05:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[refactor] AOJ の API クライアント をコンテスト単位で取得するように変更しましょう

1 participant